#include <everest/odbc/Environment.h>
#include <everest/odbc/SQLException.h>
#include <everest/odbc/Connection.h>
#include <vector>
#include <string.h>
#include <stdio.h>

using namespace NS_EVEREST::odbc;
using namespace std;


BEGIN_NAMESPACE_EVEREST
namespace odbc {
	extern Environment  g_globalEnvironment;
}
END_NAMESPACE_EVEREST

Connection::Connection()
	: m_ConHandle(SQL_NULL_HANDLE)
{
	Environment& env = g_globalEnvironment;
	SQLHDBC hConn = SQL_NULL_HANDLE;
	SQLRETURN result = SQLAllocHandle(SQL_HANDLE_DBC, env.getHandle(), &hConn);
	if (result == SQL_ERROR) {
		throw SQLException(SQL_HANDLE_ENV, env.getHandle(), "SQLAllocHandle", EVEREST_ERRSRC_ARGS);
	}
	m_ConHandle = hConn;
}

Connection::Connection(Environment& env)
	: m_ConHandle(SQL_NULL_HANDLE)
{
	SQLHDBC hConn = SQL_NULL_HANDLE;
	SQLRETURN result = SQLAllocHandle(SQL_HANDLE_DBC, env.getHandle(), &hConn);
	if (result == SQL_ERROR) {
		throw SQLException(SQL_HANDLE_ENV, env.getHandle(), "SQLAllocHandle", EVEREST_ERRSRC_ARGS);
	}
	m_ConHandle = hConn;
}

Connection::~Connection()
{
	try {
		if ( m_ConHandle != SQL_NULL_HANDLE) {
			this->close();
		}
	} catch (...) {
		// no throw
		// if close a connection which is not open, it will throw an exception
	}
	SQLFreeHandle(SQL_HANDLE_DBC, m_ConHandle);
	m_ConHandle = SQL_NULL_HANDLE;
}

SQLHDBC Connection::getHandle() const
{
	return this->m_ConHandle;
}

bool Connection::open(const char *pszConnStr)
{
	SQLRETURN result = SQLDriverConnect(
				m_ConHandle, NULL, (SQLCHAR *)(pszConnStr?pszConnStr:""),
				SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
	if ( result == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLDriverConnect", EVEREST_ERRSRC_ARGS);
	return true;
}

void Connection::close()
{
	SQLRETURN ret = SQLDisconnect(m_ConHandle);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLDisconnect", EVEREST_ERRSRC_ARGS);
	return;
}

void Connection::setAutoCommit(bool bAutoCommit)
{
	SQLPOINTER val((SQLPOINTER)(bAutoCommit?SQL_AUTOCOMMIT_ON:SQL_AUTOCOMMIT_OFF));
	SQLRETURN ret = SQLSetConnectAttr(m_ConHandle, SQL_ATTR_AUTOCOMMIT, val, 0);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLSetConnectAttr", EVEREST_ERRSRC_ARGS);
	return ;
}

bool Connection::getAutoCommit() const
{
	SQLUINTEGER val;
	SQLRETURN ret = SQLGetConnectAttr(m_ConHandle, SQL_ATTR_AUTOCOMMIT, &val, SQL_IS_UINTEGER, NULL);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLGetConnectAttr",  EVEREST_ERRSRC_ARGS);
	return val == SQL_AUTOCOMMIT_OFF?false:true;
}

void Connection::commit()
{
	SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, m_ConHandle, SQL_COMMIT);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLEndTran",  EVEREST_ERRSRC_ARGS);
	return ;
}

void Connection::rollback()
{
	SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, m_ConHandle, SQL_ROLLBACK);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLEndTran",  EVEREST_ERRSRC_ARGS);
	return ;
}

bool Connection::isActive() const
{
	SQLUINTEGER dead = SQL_CD_FALSE;
	SQLRETURN ret = SQLGetConnectAttr(this->m_ConHandle, SQL_ATTR_CONNECTION_DEAD, (SQLPOINTER*)&dead, SQL_IS_UINTEGER, NULL);
	if ( ret == SQL_ERROR )
		throw SQLException(SQL_HANDLE_DBC, m_ConHandle, "SQLGetConnectAttr(SQL_ATTR_CONNECTION_DEAD)", EVEREST_ERRSRC_ARGS);

	return (dead == SQL_CD_FALSE);
}
